package com.toulouse.recyclage.controller;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;

import android.location.Location;

import com.google.android.maps.GeoPoint;
import com.toulouse.recyclage.bo.DoubleContainer;
import com.toulouse.recyclage.bo.GlassContainer;
import com.toulouse.recyclage.bo.PackageContainer;
import com.toulouse.recyclage.bo.PackageContainer.PackagingType;
import com.toulouse.recyclage.bo.RecycleContainer;

public class RecyclePlaceController {

	public enum RecycleTypeDisplayOption {
		ALL, GLASS_CYCLE_TYPE, PACKAGING_CYCLE_TYPE;
	}

	private static RecyclePlaceController instance;

	private final List<GlassContainer> mGlassRecyclePlaces = new ArrayList<GlassContainer>();

	private final List<PackageContainer> mPackagingRecyclePlace = new ArrayList<PackageContainer>();

	private boolean glassRecyclePlacePopulated = false;

	private boolean packagingRecyclePopulated = false;

	protected RecyclePlaceController() {
		super();
	}

	public static RecyclePlaceController getInstance() {
		if (instance == null) {
			instance = new RecyclePlaceController();
		}

		return instance;
	}

	/**
	 * Populate the glass recycle list from cvs file
	 * 
	 * @param inputStream
	 *            csv file containing data
	 */
	public void populateGlassRecycle(InputStream inputStream) {

		if (!glassRecyclePlacePopulated) {

			try {

				// create BufferedReader to read csv file
				BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
				String strLine = "";
				int lineNumber = 0;

				// read comma separated file line by line
				while ((strLine = br.readLine()) != null) {
					if (lineNumber > 0) {

						// process the line, stuff to List
						String[] values = strLine.split(";");

						GlassContainer glassContainer = new GlassContainer();

						glassContainer.setCity(values[0]);
						glassContainer.setZipCode(values[1]);
						glassContainer.setStreet(values[2]);
						glassContainer.setId(values[3]);
						glassContainer.setGeoPoint(newGeoPoint(Float.parseFloat(values[7].replace(',', '.')),
								Float.parseFloat(values[6].replace(',', '.'))));

						mGlassRecyclePlaces.add(glassContainer);

					}
					lineNumber++;

				}

			} catch (Exception e) {
				System.out.println("Exception while reading csv file: " + e);
			}
		}

		glassRecyclePlacePopulated = true;
	}

	/**
	 * Populate the packaging recycle list from cvs file
	 * 
	 * @param inputStream
	 *            csv file containing data
	 */
	public void populatePackagingRecycle(InputStream inputStream) {

		if (!packagingRecyclePopulated) {

			try {

				// create BufferedReader to read csv file
				BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
				String strLine = "";
				int lineNumber = 0;

				// read comma separated file line by line
				while ((strLine = br.readLine()) != null) {
					if (lineNumber > 0) {

						// process the line, stuff to List
						String[] values = strLine.split(";");

						PackageContainer packageContainer = new PackageContainer();

						packageContainer.setCity(values[0]);
						packageContainer.setZipCode(values[1]);
						packageContainer.setStreet(values[2]);
						packageContainer.setId(values[3]);

						String dmtType = values[4];
						if (dmtType.equals("DMT_PAPIER_CARTON")) {
							packageContainer.setType(PackagingType.PAPER);
						} else if (dmtType.equals("DMT_PLASTIQUE")) {
							packageContainer.setType(PackagingType.PLASTIC);
						} else {
							packageContainer.setType(PackagingType.PACKAGING);
						}

						packageContainer.setGeoPoint(newGeoPoint(Float.parseFloat(values[8].replace(',', '.')),
								Float.parseFloat(values[7].replace(',', '.'))));

						mPackagingRecyclePlace.add(packageContainer);

					}
					lineNumber++;

				}

			} catch (Exception e) {
				System.out.println("Exception while reading csv file: " + e);
			}
		}

		packagingRecyclePopulated = true;
	}

	/**
	 * @param geoPoint
	 * @param nbStations
	 * @return list of the recycle place nearest to geoPoint
	 */
	public List<RecycleContainer> findRecyclePlaceAround(GeoPoint geoPoint, int nbStations, RecycleTypeDisplayOption recycleType) {

		List<RecycleContainer> nearestRecyclePlace = new ArrayList<RecycleContainer>();

		HashMap<RecycleContainer, Float> distanceRecyclePlace = new HashMap<RecycleContainer, Float>();

		Location originLocation = new Location("centerPoint");
		originLocation.setLatitude(geoPoint.getLatitudeE6() / 1E6);
		originLocation.setLongitude(geoPoint.getLongitudeE6() / 1E6);

		List<RecycleContainer> recylePlaces = new ArrayList<RecycleContainer>();

		if (recycleType.equals(RecycleTypeDisplayOption.GLASS_CYCLE_TYPE)) {
			recylePlaces.addAll(mGlassRecyclePlaces);
		} else if (recycleType.equals(RecycleTypeDisplayOption.PACKAGING_CYCLE_TYPE)) {
			recylePlaces.addAll(mPackagingRecyclePlace);
		} else {
			recylePlaces.addAll(mGlassRecyclePlaces);
			recylePlaces.addAll(mPackagingRecyclePlace);
		}

		for (RecycleContainer p : recylePlaces) {

			Location l = new Location(p.getId());
			l.setLatitude(p.getGeoPoint().getLatitudeE6() / 1E6);
			l.setLongitude(p.getGeoPoint().getLongitudeE6() / 1E6);

			float currentDistance = originLocation.distanceTo(l);
			distanceRecyclePlace.put(p, currentDistance);
		}

		ValueComparator comparator = new ValueComparator(distanceRecyclePlace);
		TreeMap<RecycleContainer, Float> sortedDistanceRecyclePlace = new TreeMap<RecycleContainer, Float>(comparator);

		sortedDistanceRecyclePlace.putAll(distanceRecyclePlace);

		// int index = 0;
		// for (RecycleContainer p : sortedDistanceRecyclePlace.keySet()) {
		// if (index < nbStations) {
		// nearestRecyclePlace.add(p);
		// }
		// index++;
		// }

		int index = 0;
		int placeAdded = 0;

		List<RecycleContainer> sortedDistanceRecyclePlaceKey = new ArrayList<RecycleContainer>(sortedDistanceRecyclePlace.keySet());

		while (placeAdded < nbStations && index < sortedDistanceRecyclePlaceKey.size()) {

			RecycleContainer p1 = sortedDistanceRecyclePlaceKey.get(index);

			if (index + 1 < sortedDistanceRecyclePlaceKey.size()) {
				RecycleContainer p2 = sortedDistanceRecyclePlaceKey.get(index + 1);

				if (p2.getGeoPoint().equals(p1.getGeoPoint())) {
					// Add recycle place with the both container
					nearestRecyclePlace.add(new DoubleContainer(p1, p2));
					index = index + 2;
				} else {
					nearestRecyclePlace.add(p1);
					index++;
				}
				placeAdded++;
			} else {
				nearestRecyclePlace.add(p1);
				index++;
				placeAdded++;
			}

		}

		return nearestRecyclePlace;
	}

	class ValueComparator implements Comparator<RecycleContainer> {

		HashMap<RecycleContainer, Float> map;

		public ValueComparator(HashMap<RecycleContainer, Float> base) {
			this.map = base;
		}

		@Override
		public int compare(RecycleContainer a, RecycleContainer b) {

			if (map.get(a) > map.get(b)) {
				return 1;
			} else if (map.get(a) == map.get(b)) {
				return 0;
			} else {
				return -1;
			}
		}
	}

	public static GeoPoint newGeoPoint(float lat, float lng) {
		return new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
	}

	public List<GlassContainer> getGlassRecyclePlace() {
		return mGlassRecyclePlaces;
	}

	public List<PackageContainer> getPackagingRecyclePlace() {
		return mPackagingRecyclePlace;
	}

}
